package net.sureon.common.exception;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.exception.NestableException;

/**
 * This is base Exception for System. All system exception should extend it.
 * 
 * @author jason
 * 
 */
public class SureonException extends NestableException implements
		java.io.Serializable {

	/**
	 * Serial Version UID
	 */
	private static final long serialVersionUID = -5745331324487158202L;
	private Map<String, Object> extraInfo = null;

	/**
	 * Arguments that would be passed with the error code for formatting the
	 * error messages *
	 */
	private List arguments = new ArrayList();

	/**
	 * Hide constructors that users should not be using *
	 */
	private SureonException() {
	};

	/**
	 * Constructor that sets the error key
	 * 
	 * @param message
	 *            Message to report for the exception.
	 */
	public SureonException(String message) {
		super(message);
	}

	/**
	 * Constructor that sets the error key and takes a map of extra info.
	 * 
	 * @param message
	 *            of the execption
	 * @param extraInfo
	 *            additional info as a map
	 */
	public SureonException(String message, Map extraInfo) {
		super(message);
		this.extraInfo = extraInfo;
	}

	/**
	 * Constructor based on an existing exception
	 * 
	 * @param cause
	 *            The root exception to chain.
	 */
	public SureonException(Throwable cause) {
		super(cause);
	};

	/**
	 * Constructor based on a message and an existing exception.
	 * 
	 * @param message
	 *            Message to use for the exception
	 * @param cause
	 *            The root caues to chain
	 */
	public SureonException(String message, Throwable cause) {
		super(message, cause);
	}

	/**
	 * Constructor based on a message and a nested exception. This version also
	 * takes a map of extra info.
	 * 
	 * @param message
	 *            of the execption
	 * @param cause
	 *            the Throwable being wrapped
	 * @param extraInfo
	 *            additional info as a map
	 */
	public SureonException(String message, Throwable cause, Map extraInfo) {
		super(message, cause);
		this.extraInfo = extraInfo;
	}

	/**
	 * If you want variable length params after constuction (required if using
	 * descendent classes, call these methods. To set a previous exception, call
	 * initCause() after creating with one of these. (Arguments are placed
	 * within message text strings as defined by the java.text.MessageFormat
	 * class)
	 * 
	 * @param message
	 *            The error code to lookup in the properties file
	 * @param arg0
	 *            Argument to pass to message format for formatting the string
	 *            retrieved by the error lookup
	 */
	public SureonException(String message, Object arg0) {
		super(message);
		setArguments(arg0);
	}

	/**
	 * If you want variable length params after constuction (required if using
	 * descendent classes, call these methods. To set a previous exception, call
	 * initCause() after creating with one of these. (Arguments are placed
	 * within message text strings as defined by the java.text.MessageFormat
	 * class)
	 * 
	 * @param message
	 *            The error code to lookup in the properties file
	 * @param arg0
	 *            Argument to pass to message format for formatting the string
	 *            retrieved by the error lookup
	 * @param arg1
	 *            Argument to pass to message format for formatting the string
	 *            retrieved by the error lookup
	 */
	public SureonException(String message, Object arg0, Object arg1) {
		super(message);

		setArguments(arg0, arg1);
	}

	/**
	 * If you want variable length params after constuction (required if using
	 * descendent classes, call these methods. To set a previous exception, call
	 * initCause() after creating with one of these. (Arguments are placed
	 * within message text strings as defined by the java.text.MessageFormat
	 * class)
	 * 
	 * @param message
	 *            The error code to lookup in the properties file
	 * @param arg0
	 *            Argument to pass to message format for formatting the string
	 *            retrieved by the error lookup
	 * @param arg1
	 *            Argument to pass to message format for formatting the string
	 *            retrieved by the error lookup
	 * @param arg2
	 *            Argument to pass to message format for formatting the string
	 *            retrieved by the error lookup
	 */
	public SureonException(String message, Object arg0, Object arg1, Object arg2) {
		super(message);

		setArguments(arg0, arg1, arg2);
	}

	/**
	 * If you want variable length params after constuction (required if using
	 * descendent classes, call these methods. To set a previous exception, call
	 * initCause() after creating with one of these. (Arguments are placed
	 * within message text strings as defined by the java.text.MessageFormat
	 * class)
	 * 
	 * @param message
	 *            The error code to lookup in the properties file
	 * @param arg0
	 *            Argument to pass to message format for formatting the string
	 *            retrieved by the error lookup
	 * @param arg1
	 *            Argument to pass to message format for formatting the string
	 *            retrieved by the error lookup
	 * @param arg2
	 *            Argument to pass to message format for formatting the string
	 *            retrieved by the error lookup
	 * @param arg3
	 *            Argument to pass to message format for formatting the string
	 *            retrieved by the error lookup
	 */
	public SureonException(String message, Object arg0, Object arg1,
			Object arg2, Object arg3) {
		super(message);

		setArguments(arg0, arg1, arg2, arg3);
	}

	/**
	 * If you want variable length params after constuction (required if using
	 * descendent classes, call these methods. To set a previous exception, call
	 * initCause() after creating with one of these. (Arguments are placed
	 * within message text strings as defined by the java.text.MessageFormat
	 * class)
	 * 
	 * @param message
	 *            The error code to lookup in the properties file
	 * @param args
	 *            Arguments to pass to message format for formatting the string
	 *            retrieved by the error lookup
	 */
	public SureonException(String message, List args) {
		super(message);
		arguments.addAll(args);
	}

	/**
	 * If you want variable length params after constuction (required if using
	 * descendent classes, call these methods. (Arguments are placed within
	 * message text strings as defined by the java.text.MessageFormat class)
	 * 
	 * @param arg0
	 *            Argument to pass to message format for formatting the string
	 *            retrieved by the error lookup
	 * @return SureonException instance of self
	 */
	public SureonException setArguments(Object arg0) {
		arguments.add(arg0);
		return this;
	}

	/**
	 * If you want variable length params after constuction (required if using
	 * descendent classes, call these methods. (Arguments are placed within
	 * message text strings as defined by the java.text.MessageFormat class)
	 * 
	 * @param arg0
	 *            Argument to pass to message format for formatting the string
	 *            retrieved by the error lookup
	 * @param arg1
	 *            Argument to pass to message format for formatting the string
	 *            retrieved by the error lookup
	 * @return SureonException instance of self
	 */
	public SureonException setArguments(Object arg0, Object arg1) {
		arguments.add(arg0);
		arguments.add(arg1);
		return this;
	}

	/**
	 * If you want variable length params after constuction (required if using
	 * descendent classes, call these methods. (Arguments are placed within
	 * message text strings as defined by the java.text.MessageFormat class)
	 * 
	 * @param arg0
	 *            Argument to pass to message format for formatting the string
	 *            retrieved by the error lookup
	 * @param arg1
	 *            Argument to pass to message format for formatting the string
	 *            retrieved by the error lookup
	 * @param arg2
	 *            Argument to pass to message format for formatting the string
	 *            retrieved by the error lookup
	 * @return SureonException instance of self
	 */
	public SureonException setArguments(Object arg0, Object arg1, Object arg2) {
		arguments.add(arg0);
		arguments.add(arg1);
		arguments.add(arg2);
		return this;
	}

	/**
	 * If you want variable length params after constuction (required if using
	 * descendent classes, call these methods. (Arguments are placed within
	 * message text strings as defined by the java.text.MessageFormat class)
	 * 
	 * @param arg0
	 *            Argument to pass to message format for formatting the string
	 *            retrieved by the error lookup
	 * @param arg1
	 *            Argument to pass to message format for formatting the string
	 *            retrieved by the error lookup
	 * @param arg2
	 *            Argument to pass to message format for formatting the string
	 *            retrieved by the error lookup
	 * @param arg3
	 *            Argument to pass to message format for formatting the string
	 *            retrieved by the error lookup
	 * @return SureonException instance of self
	 */
	public SureonException setArguments(Object arg0, Object arg1, Object arg2,
			Object arg3) {
		arguments.add(arg0);
		arguments.add(arg1);
		arguments.add(arg2);
		arguments.add(arg3);
		return this;
	}

	/**
	 * If you want variable length params after constuction (required if using
	 * descendent classes, call these methods. (Arguments are placed within
	 * message text strings as defined by the java.text.MessageFormat class)
	 * 
	 * @param args
	 *            Arguments to pass to message format for formatting the string
	 *            retrieved by the error lookup
	 * @return SureonException instance of self
	 */
	public SureonException setArguments(List args) {
		arguments.clear();
		arguments.addAll(args);
		return this;
	}

	/**
	 * Returns the arguments that may have been set with the exception
	 * 
	 * @return List Array of arguments that may have been set
	 */
	public List getArguments() {
		return arguments;
	}

	/**
	 * Appends a key / value pair to the extraInfo map. Returns the exception so
	 * that you can cascade append calls. Example: <p/> <code>
     * (new SureonException("xyz")).appendExtraInfo("abc", "def").appendExtraInfo("qqq", "zzz");
     * </code>
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public SureonException appendExtraInfo(String key, Object value) {
		if (extraInfo == null) {
			extraInfo = new HashMap();
		}
		extraInfo.put(key, value);
		return this;
	}

	/**
	 * Formats the extra info into a string array.
	 * 
	 * @return string array of extra info.
	 */
	public String[] printExtraInfo() {
		String[] returnThis = null;
		if (extraInfo == null) {
			returnThis = new String[1];
			returnThis[0] = "";
		} else {
			returnThis = new String[extraInfo.size()];
			Iterator eInfoIter = extraInfo.keySet().iterator();
			Object itemName, itemValue;
			int idx = 0;
			while (eInfoIter.hasNext()) {
				itemName = eInfoIter.next();
				itemValue = extraInfo.get(itemName);
				returnThis[idx] = ((itemName == null) ? "" : itemName
						.toString())
						+ " = "
						+ ((itemValue == null) ? "" : itemValue.toString());
				idx++;
			}
		}
		return returnThis;
	}
	
	public String getErrorDisplay(){
		return ExceptionUtil.translateExceptionKey(getMessage(), arguments.toArray());
	}
}
